/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

XilinxFPGAToolchain::XilinxFPGAToolchain()
{
	string root = "/opt/Xilinx";
	DIR* hdir = opendir(root.c_str());
	if(!hdir)
		FatalError("Directory %s could not be opened\n", root.c_str());
	
	dirent ent;
	dirent* pent;
	int major_best = 0;
	int minor_best = 0;
	while(0 == readdir_r(hdir, &ent, &pent))
	{
		if(pent == NULL)
			break;
		if(ent.d_name[0] == '.')
			continue;
		
		int major;
		int minor;
		if(2 != sscanf(ent.d_name, "%d.%d", &major, &minor))
			continue;

		//Check if we got a better version
		if(major > major_best)
		{
			major_best = major;
			minor_best = minor;
		}
		else if( (major == major_best) && (minor > minor_best) )
			minor_best = minor;
		else
		{
			//other version is better, keep it
		}
	}
	closedir(hdir);
	
	Init(major_best, minor_best);
}

XilinxFPGAToolchain::XilinxFPGAToolchain(int major, int minor)
{
	Init(major, minor);
}

void XilinxFPGAToolchain::Init(int major, int minor)
{	
	//Save the version number
	string root = "/opt/Xilinx";
	char path[128];
	snprintf(path, sizeof(path), "%s/%d.%d", root.c_str(), major, minor);
	if(!DoesDirectoryExist(path))
		FatalError("XilinxFPGAToolchain: Cannot use version %d.%d because %s does not exist\n", major, minor, path);
	m_rootpath = path;
	
	//Print debug stuff
	DebugPrintfHeading(2, "Initializing Xilinx FPGA toolchain...\n");
	DebugPrintfSilent( 3, "    Using toolchain version %d.%d\n", major, minor);
	
	//Figure out current architecture
	string arch = "lin";
	#if __x86_64__
		arch = "lin64";
	#endif
	
	string binpath = m_rootpath + "/ISE_DS/ISE/bin/" + arch;
	
	//Find tools
	m_fusepath = binpath + "/fuse";
	m_xstpath		= binpath + "/xst";
	m_ngdbuildpath	= binpath + "/ngdbuild";
	m_mappath		= binpath + "/map";
	m_parpath		= binpath + "/par";
	m_trcepath		= binpath + "/trce";
	m_bitgenpath	= binpath + "/bitgen";
	m_cpldfitpath	= binpath + "/cpldfit";
	m_hprep6path	= binpath + "/hprep6";
	if(!DoesFileExist(m_fusepath))
		FatalError("XilinxFPGAToolchain: cannot find fuse at %s\n", m_fusepath.c_str());
	if(!DoesFileExist(m_xstpath))
		FatalError("XilinxFPGAToolchain: cannot find xst at %s\n", m_xstpath.c_str());
	if(!DoesFileExist(m_ngdbuildpath))
		FatalError("XilinxFPGAToolchain: cannot find ngdbuild at %s\n", m_ngdbuildpath.c_str());
	if(!DoesFileExist(m_mappath))
		FatalError("XilinxFPGAToolchain: cannot find map at %s\n", m_mappath.c_str());
	if(!DoesFileExist(m_parpath))
		FatalError("XilinxFPGAToolchain: cannot find par at %s\n", m_parpath.c_str());
	if(!DoesFileExist(m_trcepath))
		FatalError("XilinxFPGAToolchain: cannot find trce at %s\n", m_trcepath.c_str());
	if(!DoesFileExist(m_bitgenpath))
		FatalError("XilinxFPGAToolchain: cannot find bitgen at %s\n", m_bitgenpath.c_str());
	if(!DoesFileExist(m_cpldfitpath))
		FatalError("XilinxFPGAToolchain: cannot find cpldfit at %s\n", m_cpldfitpath.c_str());
	if(!DoesFileExist(m_hprep6path))
		FatalError("XilinxFPGAToolchain: cannot find hprep6 at %s\n", m_hprep6path.c_str());
}

XilinxFPGAToolchain::~XilinxFPGAToolchain()
{
	
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Accessors

std::string XilinxFPGAToolchain::GetDescription() const
{
	return m_rootpath;
}

std::string XilinxFPGAToolchain::GetHash() const
{
	//TODO: include more detail?
	return sha256(m_rootpath);
}

string XilinxFPGAToolchain::GetSynthesisFlagHash(const FPGASynthesisFlagList& flags)
{
	string hashin;
	for(size_t i=0; i<flags.size(); i++)
		hashin += sha256(flags[i]->toString(this));
	return sha256(hashin);
}

string XilinxFPGAToolchain::GetArchitecture(string device) const
{
	if(device.find("xc2c") == 0)
		return string("xilinx-cpld-cr2-") + device;
	else if(device.find("xc3s") == 0)
		return string("xilinx-fpga-spartan3-") + device;
	else if(device.find("xc6s") == 0)
		return string("xilinx-fpga-spartan6-") + device;
	else if(device.find("xc7a") == 0)
		return string("xilinx-fpga-artix7-") + device;
	else if(device.find("xc7k") == 0)
		return string("xilinx-fpga-kintex7-") + device;
	else
		FatalError("XilinxFPGAToolchain: unrecognized device %s\n", device.c_str());
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Factories

XilinxFPGAToolchain* XilinxFPGAToolchain::CreateDefaultToolchainCached()
{
	if(m_cachedToolchains.find("default_xilinx") != m_cachedToolchains.end())
		return dynamic_cast<XilinxFPGAToolchain*>(m_cachedToolchains["default_xilinx"]);
	return CreateDefaultToolchain();
}

XilinxFPGAToolchain* XilinxFPGAToolchain::CreateDefaultToolchain()
{
	XilinxFPGAToolchain* chain = new XilinxFPGAToolchain;
	DebugPrintfHeading(2, "Selected default Xilinx FPGA toolchain\n");
	DebugPrintfSilent(2, "    %s\n", chain->GetDescription().c_str());
	m_cachedToolchains["default_xilinx"] = chain;
	return chain;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation

ClusterJob* XilinxFPGAToolchain::CompileSourcesToNativeSimulation(
		vector<string> source_files,
		string output_filename,
		const vector<ClusterJob*>& deps,
		std::string top_level,
		std::string device,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Compiling simulation file %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Write the .prj file
	string prj_file = path + "/" + base + ".prj";
	FILE* fp = fopen(prj_file.c_str(), "w");
	if(!fp)
		FatalError("Failed to create project file %s\n", prj_file.c_str());
	for(size_t i=0; i<source_files.size(); i++)
		fprintf(fp, "verilog work \"%s\"\n", source_files[i].c_str());
	fprintf(fp, "verilog work \"%s/ISE_DS/ISE/verilog/src/glbl.v\"\n", m_rootpath.c_str());
	fclose(fp);
	
	//Create the batch script
	string bin_fname = output_filename + ".bin";
	string batch_fname = path + "/fuse_batch_command_" + base + ".sh";
	fp = fopen(batch_fname.c_str(), "w");
	if(!fp)
		FatalError("Failed to create batch script %s\n", batch_fname.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "%s -o %s -prj %s work.%s work.glbl \\\n",
		m_fusepath.c_str(),
		bin_fname.c_str(),
		prj_file.c_str(),
		top_level.c_str());
	//default to speed grade 1 for simulations for now
	if(device.find("xc3s") == 0)
		fprintf(fp, "-d XILINX_FPGA=1 -d XILINX_SPARTAN3=1 -d XILINX_SPEEDGRADE=1\\\n");
	else if(device.find("xc6s") == 0)
		fprintf(fp, "-d XILINX_FPGA=1 -d XILINX_SPARTAN6=1 -d XILINX_SPEEDGRADE=1 \\\n");
	else if(device.find("xc7a") == 0)
		fprintf(fp, "-d XILINX_FPGA=1 -d XILINX_7SERIES -d XILINX_ARTIX7=1 -d XILINX_SPEEDGRADE=1 \\\n");
	else if(device.find("xc7k") == 0)
		fprintf(fp, "-d XILINX_FPGA=1 -d XILINX_7SERIES -d XILINX_KINTEX7=1 -d XILINX_SPEEDGRADE=1 \\\n");
	else if(device.find("xc7v") == 0)
		fprintf(fp, "-d XILINX_FPGA=1 -d XILINX_7SERIES -d XILINX_VIRTEX7=1 -d XILINX_SPEEDGRADE=1 \\\n");
	else if(device.find("xc2c") == 0)
		fprintf(fp, "-d XILINX_CPLD=1 -d XILINX_COOLRUNNER2=1 -d XILINX_SPEEDGRADE=1 \\\n");
	fprintf(fp, "    -intstyle ise -incremental -lib unisims_ver -lib unimacro_ver -lib xilinxcorelib_ver -lib secureip \\\n");
	fprintf(fp, "    -i %s/generic/include\\\n", project_root.c_str());
	fprintf(fp, "    2>&1\n");
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(batch_fname.c_str(), 0755))
		FatalError("Failed to set execute permissions on batch script %s\n", batch_fname.c_str());
	
	//Create the tcl script
	string tcl_fname = output_filename + ".tcl";
	if(!fp)
		FatalError("Failed to create tcl script %s\n", tcl_fname.c_str());
	fp = fopen(tcl_fname.c_str(), "w");
	fprintf(fp, "onerror {resume}\n");
	fprintf(fp, "wave add /\n");
	fprintf(fp, "run all;\n");
	fprintf(fp, "exit;\n");
	fclose(fp);
	
	//Create the run-test wrapper script
	string vcd_fname = output_filename + ".vcd";
	fp = fopen(output_filename.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", output_filename.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "unset LD_LIBRARY_PATH\n");
	fprintf(fp, "source %s/ISE_DS/settings64.sh\n", m_rootpath.c_str());
	fprintf(fp, "cd %s\n", path.c_str());
	fprintf(fp, "%s -tclbatch %s -intstyle silent -vcdfile %s -vcdunit ps || exit 1\n",
		bin_fname.c_str(),
		tcl_fname.c_str(),
		vcd_fname.c_str());
	fprintf(fp, "cat isim.log | grep -q PASS\n");
	fprintf(fp, "if [ \"$?\" != \"1\" ]; then\n");
	fprintf(fp, "    exit 0;\n");
	fprintf(fp, "fi\n");
	fprintf(fp, "exit 1;\n");
	fclose(fp);
	if(0 != chmod(output_filename.c_str(), 0755))
		FatalError("Failed to set execute permissions on test wrapper script %s\n", output_filename.c_str());

	//Submit the batch script
	return cluster->SubmitBatchJob(batch_fname, cluster->m_fpgaBuildPartition, path, deps);
}

ClusterJob* XilinxFPGAToolchain::SynthesizeSourcesToUnplacedNetlist(
		vector<string> source_files,
		string output_filename,
		const vector<ClusterJob*>& deps,
		string top_level,
		string device,
		FPGASynthesisFlagList flags,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Synthesizing NGC netlist file %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Write the .prj file
	string prj_file = path + "/" + base + ".prj";
	FILE* fp = fopen(prj_file.c_str(), "w");
	if(!fp)
		FatalError("Failed to create project file %s\n", prj_file.c_str());
	for(size_t i=0; i<source_files.size(); i++)
		fprintf(fp, "verilog work \"%s\"\n", source_files[i].c_str());
	fprintf(fp, "verilog work \"%s/ISE_DS/ISE/verilog/src/glbl.v\"\n", m_rootpath.c_str());
	fclose(fp);
	
	//Generate file names and make temporary directories
	string ngc_file = path + "/" + base + ".ngc";
	string xst_file = path + "/" + base + ".xst";
	string xst_dir = path + "/" + base + "_xst";
	string xst_tmpdir = path + "/projnav.tmp";
	if( (0 != mkdir(xst_dir.c_str(), 0755)) && (errno != EEXIST) )
		FatalError("Could not create XST directory %s\n", xst_dir.c_str());
	if( (0 != mkdir(xst_tmpdir.c_str(), 0755)) && (errno != EEXIST) )
		FatalError("Could not create XST temporary directory %s\n", xst_tmpdir.c_str());

	//Parse out the FPGA speed grade
	//Speed grade is the first digit after the first dash in the device name
	int speedgrade = 0;
	if(1 != sscanf(device.c_str(), "%*[^-]-%d", &speedgrade))
		FatalError("Unknown speed grade for device %s\n", device.c_str());

	//Create the XST input script
	fp = fopen(xst_file.c_str(), "w");
	if(!fp)
		FatalError("Failed to create XST script %s\n", xst_file.c_str());
	fprintf(fp, "set -tmpdir \"%s\"\n", xst_tmpdir.c_str());		//temporary directory
	fprintf(fp, "set -xsthdpdir \"%s\"\n", xst_dir.c_str());		//work directory
	fprintf(fp, "run\n");
	fprintf(fp, "-ifn %s\n", prj_file.c_str());						//list of sources
	fprintf(fp, "-ofn %s\n", ngc_file.c_str());						//netlist path
	fprintf(fp, "-ofmt NGC\n");										//Xilinx NGC netlist format
	fprintf(fp, "-p %s\n", device.c_str());							//part number
	fprintf(fp, "-top %s\n", top_level.c_str());					//top level module
	fprintf(fp, "-vlgincdir %s/generic/include\n", project_root.c_str());	//verilog include directory
	fprintf(fp, "-hierarchy_separator /\n");						//use / as hierarchy separator
	fprintf(fp, "-bus_delimiter []\n");								//use [] as bus delimeter
	fprintf(fp, "-case maintain\n");								//keep cases as is
	fprintf(fp, "-rtlview no\n");									//do not generate RTL schematic
	if(device.find("xc3s") == 0)
		fprintf(fp, "-define {XILINX_FPGA XILINX_SPARTAN3 XILINX_SPEEDGRADE=%d}\n", speedgrade);
	else if(device.find("xc6s") == 0)
		fprintf(fp, "-define {XILINX_FPGA XILINX_SPARTAN6 XILINX_SPEEDGRADE=%d}\n", speedgrade);
	else if(device.find("xc7a") == 0)
		fprintf(fp, "-define {XILINX_FPGA XILINX_7SERIES XILINX_ARTIX7 XILINX_SPEEDGRADE=%d}\n", speedgrade);
	else if(device.find("xc7k") == 0)
		fprintf(fp, "-define {XILINX_FPGA XILINX_7SERIES XILINX_KINTEX7 XILINX_SPEEDGRADE=%d}\n", speedgrade);
	else if(device.find("xc7v") == 0)
		fprintf(fp, "-define {XILINX_FPGA XILINX_7SERIES XILINX_VIRTEX7 XILINX_SPEEDGRADE=%d}\n", speedgrade);
	else if(device.find("xc2c") == 0)
		fprintf(fp, "-define {XILINX_CPLD XILINX_COOLRUNNER2 XILINX_SPEEDGRADE=%d}\n", speedgrade);
		
	if(device.find("xc2c") == string::npos)							//FPGA-specific flags
	{
		fprintf(fp, "-glob_opt AllClockNets\n");						//optimize clock periods
	}
	fprintf(fp, "-loop_iteration_limit 131072\n");					//loops can run up to 128K cycles
	for(size_t i=0; i<flags.size(); i++)
		fprintf(fp, "%s", flags[i]->toString(this).c_str());
	fclose(fp);
	
	//Create the run-xst script
	string xst_syr_file = path + "/" + base + ".syr";
	string xst_runscript = path + "/" + base + "_xst.sh";
	fp = fopen(xst_runscript.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", xst_runscript.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "cd %s\n", path.c_str());
	fprintf(fp, "%s -intstyle xflow -ifn %s -ofn %s\n",
		m_xstpath.c_str(),
		xst_file.c_str(),
		xst_syr_file.c_str()
		);
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(xst_runscript.c_str(), 0755))
		FatalError("Failed to set execute permissions on XST run script %s\n", xst_runscript.c_str());
	
	//Submit the batch script
	return cluster->SubmitBatchJob(xst_runscript, cluster->m_fpgaBuildPartition, path, deps, 1, 1024);
}

ClusterJob* XilinxFPGAToolchain::TranslateNGCNetlistToNGDNetlist(
		string ngcfile,
		string constraints,
		string output_filename,
		const vector<ClusterJob*>& deps,
		string device,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Translating NGD netlist file %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	
	string inpath = GetDirOfFile(ngcfile);
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Create the run-ngdbuild script
	string ngdbuild_runscript = path + "/" + base + "_ngdbuild.sh";
	FILE* fp = fopen(ngdbuild_runscript.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", ngdbuild_runscript.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "cd %s\n", path.c_str());
	fprintf(fp, "%s -intstyle ise -dd _ngo -nt timestamp -uc %s -p %s %s %s\n",
		m_ngdbuildpath.c_str(),
		constraints.c_str(),
		device.c_str(),
		ngcfile.c_str(),
		output_filename.c_str()
		);
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(ngdbuild_runscript.c_str(), 0755))
		FatalError("Failed to set execute permissions on ngdbuild run script %s\n", ngdbuild_runscript.c_str());
	
	//Submit the batch script
	return cluster->SubmitBatchJob(ngdbuild_runscript, cluster->m_fpgaBuildPartition, path, deps, 1, 512);
}

ClusterJob* XilinxFPGAToolchain::MapNetlist(
		string ngdfile,
		string output_filename,
		const vector<ClusterJob*>& deps,
		string device,
		XilinxFPGAMapFlagList flags,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Mapping NCD netlist file %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	string pcf_file = path + "/" + base + ".pcf";
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Create the run-map script
	string map_runscript = path + "/" + base + "_map.sh";
	FILE* fp = fopen(map_runscript.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", map_runscript.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "cd %s\n", path.c_str());
	string sflags = "";
	for(size_t i=0; i<flags.size(); i++)
		sflags += flags[i]->toString(this);
	if(device.find("xc3s") == 0)
	{
		//Map:275 - The Multi threading option (-mt on|off) is not supported for this architecture
		//So we need to disable it
		fprintf(fp, "%s -intstyle ise -p %s %s -detail -o %s %s %s\n",
			m_mappath.c_str(),
			device.c_str(),
			sflags.c_str(),
			output_filename.c_str(),
			ngdfile.c_str(),
			pcf_file.c_str()
			);
	}
	else
	{
		fprintf(fp, "%s -intstyle ise -p %s -mt 2 %s -detail -o %s %s %s\n",
			m_mappath.c_str(),
			device.c_str(),
			sflags.c_str(),
			output_filename.c_str(),
			ngdfile.c_str(),
			pcf_file.c_str()
			);
	}
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(map_runscript.c_str(), 0755))
		FatalError("Failed to set execute permissions on map run script %s\n", map_runscript.c_str());
	
	//Submit the batch script
	return cluster->SubmitBatchJob(map_runscript, cluster->m_fpgaBuildPartition, path, deps, 1, 2048);
}

ClusterJob* XilinxFPGAToolchain::PlaceAndRouteNetlist(
		string input_netlist,
		string constraints,
		string output_filename,
		string device,
		const vector<ClusterJob*>& deps,
		FPGAPlaceAndRouteFlagList flags,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Place and route NCD netlist file %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Create the run-par script
	string par_runscript = path + "/" + base + "_par.sh";
	FILE* fp = fopen(par_runscript.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", par_runscript.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "cd %s\n", path.c_str());
	if(flags.size() != 0)
		FatalError("XilinxFPGAToolchain: par flags not implemented yet\n");
	if(device.find("xc3s") == 0)
	{
		//Avoid warning
		//Par:493 - Multi-threading ("-mt" option) is not supported for this architecture. PAR will use only one processor.
		fprintf(fp, "%s -intstyle ise %s %s %s\n",
			m_parpath.c_str(),
			input_netlist.c_str(),
			output_filename.c_str(),
			constraints.c_str()
			);
	}
	else
	{
		fprintf(fp, "%s -intstyle ise -mt 4 %s %s %s\n",
			m_parpath.c_str(),
			input_netlist.c_str(),
			output_filename.c_str(),
			constraints.c_str()
			);
	}
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(par_runscript.c_str(), 0755))
		FatalError("Failed to set execute permissions on par run script %s\n", par_runscript.c_str());
	
	//Submit the batch script
	return cluster->SubmitBatchJob(par_runscript, cluster->m_fpgaBuildPartition, path, deps, 2, 4096);
}

ClusterJob* XilinxFPGAToolchain::StaticTimingAnalysis(
		string input_netlist,
		string constraints,
		string output_filename,
		int speedgrade,
		const vector<ClusterJob*>& deps,
		FPGATimingAnalysisFlagList flags,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Run static timing analysis %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	string outbase = path + "/" + base;
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Create the run-trce script
	string trce_runscript = path + "/" + base + "_trce.sh";
	FILE* fp = fopen(trce_runscript.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", trce_runscript.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "cd %s\n", path.c_str());
	if(flags.size() != 0)
		FatalError("XilinxFPGAToolchain: trce flags not implemented yet\n");
	fprintf(fp, "%s -intstyle ise -v 12 -s %d -n 3 -fastpaths -xml %s %s -o %s %s\n",
		m_trcepath.c_str(),
		speedgrade,
		outbase.c_str(),
		input_netlist.c_str(),
		output_filename.c_str(),
		constraints.c_str()
		);
	fprintf(fp, "cat *.twr\n");
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(trce_runscript.c_str(), 0755))
		FatalError("Failed to set execute permissions on trce run script %s\n", trce_runscript.c_str());
	
	//Submit the batch script
	return cluster->SubmitBatchJob(trce_runscript, cluster->m_fpgaBuildPartition, path, deps, 1, 1024);
}

ClusterJob* XilinxFPGAToolchain::GenerateBitstream(
		string input_netlist,
		string constraints,
		string output_filename,
		string device,
		const vector<ClusterJob*>& deps,
		FPGABitstreamGenerationFlagList flags,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Generate FPGA bitstream %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	string outbase = path + "/" + base;
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Create bitgen input script
	string script_file = path + "/" + base + ".ut";
	FILE* fp = fopen(script_file.c_str(), "w");
	if(!fp)
		FatalError("Failed to create bitgen script %s\n", script_file.c_str());
	fprintf(fp, "-w\n");
	if(device.find("xc6s") == 0)
		fprintf(fp, "-g INIT_9K:Yes\n");
	fprintf(fp, "-g DonePipe:Yes\n");	//non-default
	fprintf(fp, "-g DriveDone:Yes\n");	//non-default
	//fprintf(fp, "-g ReadBack\n");	//Generate readback files
	//fprintf(fp, "-m\n");				//Generate readback mask
	//fprintf(fp, "-l\n");				//Generate readback map
	for(size_t i=0; i<flags.size(); i++)
		fprintf(fp, "%s\n", flags[i]->toString(this).c_str());

	/*
		Flags to implement:
			ConfigRate
			UnusedPin
			UserID
			StartUpClk
			LCK_cycle
			Security
			DonePipe
			DriveDone
			en_sw_gsr
			drive_awake
			sw_clk
	 */

	fclose(fp);
	
	//Create the run-bitgen script
	string bitgen_runscript = path + "/" + base + "_bitgen.sh";
	fp = fopen(bitgen_runscript.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", bitgen_runscript.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "cd %s\n", path.c_str());
	fprintf(fp, "%s -intstyle ise -f %s %s %s %s\n",
		m_bitgenpath.c_str(),
		script_file.c_str(),
		input_netlist.c_str(),
		output_filename.c_str(),
		constraints.c_str()
		);
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(bitgen_runscript.c_str(), 0755))
		FatalError("Failed to set execute permissions on bitgen run script %s\n", bitgen_runscript.c_str());
	
	//Submit the batch script
	return cluster->SubmitBatchJob(bitgen_runscript, cluster->m_fpgaBuildPartition, path, deps, 1, 2048);
}

ClusterJob* XilinxFPGAToolchain::FitCPLDNetlist(
		string input_netlist,
		string output_filename,
		string device,
		const vector<ClusterJob*>& deps,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Fit CPLD netlist %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	string outbase = path + "/" + base;
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Create the run-cpldfit script
	string cpldfit_runscript = path + "/" + base + "_cpldfit.sh";
	FILE* fp = fopen(cpldfit_runscript.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", cpldfit_runscript.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "cd %s\n", path.c_str());
	//if(flags.size() != 0)
	//	FatalError("XilinxFPGAToolchain: cpldfit flags not implemented yet\n");
	fprintf(fp, "%s -intstyle ise -p %s -ofmt verilog -optimize density -loc on -slew fast -init low -unused pullup -terminate pullup -wysiwyg -iostd LVCMOS18 %s\n",
		m_cpldfitpath.c_str(),
		device.c_str(),
		input_netlist.c_str()
		);
	fprintf(fp, "cat %s.rpt\n", outbase.c_str());
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(cpldfit_runscript.c_str(), 0755))
		FatalError("Failed to set execute permissions on cpldfit run script %s\n", cpldfit_runscript.c_str());
	
	//Submit the batch script
	return cluster->SubmitBatchJob(cpldfit_runscript, cluster->m_fpgaBuildPartition, path, deps, 1, 512);
}

ClusterJob* XilinxFPGAToolchain::GenerateCPLDBitstream(
		string input_netlist,
		string output_filename,
		const vector<ClusterJob*>& deps,
		Cluster* cluster
		)
{
	//Print the heading
	DebugPrintfHeading(3, "Generate CPLD programming file %s\n", output_filename.c_str());
	
	//Get output directory
	string path = GetDirOfFile(output_filename);
	string base = GetBasenameOfFileWithoutExt(output_filename);
	string outbase = path + "/" + base;
	
	//Get the project root directory
	string project_root = CanonicalizePath(path + "/../..");
	
	//Create the run-hprep6 script
	string hprep6_runscript = path + "/" + base + "_hprep6.sh";
	FILE* fp = fopen(hprep6_runscript.c_str(), "w");
	if(!fp)
		FatalError("Failed to create wrapper script %s\n", hprep6_runscript.c_str());
	fprintf(fp, "#!/bin/bash\n");
	fprintf(fp, "cd %s\n", path.c_str());
	//if(flags.size() != 0)
	//	FatalError("XilinxFPGAToolchain: hprep6 flags not implemented yet\n");
	fprintf(fp, "%s -s IEEE1149 -i %s\n",
		m_hprep6path.c_str(),
		input_netlist.c_str()
		);
	fprintf(fp, "if [ $? -ne 0 ]; then\n");
	fprintf(fp, "    touch build_failed\n");
	fprintf(fp, "    exit 1\n");
	fprintf(fp, "else\n");
	fprintf(fp, "    touch build_successful\n");
	fprintf(fp, "fi\n");
	fclose(fp);
	if(0 != chmod(hprep6_runscript.c_str(), 0755))
		FatalError("Failed to set execute permissions on hprep6 run script %s\n", hprep6_runscript.c_str());
	
	//Submit the batch script
	return cluster->SubmitBatchJob(hprep6_runscript, cluster->m_fpgaBuildPartition, path, deps, 1, 512);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Flags

XilinxMapGlobalOptimizationFlag::XilinxMapGlobalOptimizationFlag(XilinxMapGlobalOptimizationFlag::OptMode mode, bool retiming)
	: m_mode(mode)
	, m_retiming(retiming)
{
}

XilinxMapGlobalOptimizationFlag::~XilinxMapGlobalOptimizationFlag()
{
}

XilinxFPGAMapFlag* XilinxMapGlobalOptimizationFlag::Clone() const
{
	return new XilinxMapGlobalOptimizationFlag(m_mode, m_retiming);
}

string XilinxMapGlobalOptimizationFlag::toString(const XilinxFPGAToolchain* /*toolchain*/)
{
	string retval = "";
	
	switch(m_mode)
	{
	case OPT_SPEED:
		retval += "-global_opt speed ";
		break;
	case OPT_AREA:
		retval += "-global_opt area ";
		break;
	case OPT_POWER:
		retval += "-global_opt power ";
		break;
	default:
		FatalError("XilinxMapGlobalOptimizationFlag: unknown value\n");
	}
	return retval;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

XilinxMapIgnoreHierarchyFlag::XilinxMapIgnoreHierarchyFlag()
{
}

XilinxMapIgnoreHierarchyFlag::~XilinxMapIgnoreHierarchyFlag()
{
}

XilinxFPGAMapFlag* XilinxMapIgnoreHierarchyFlag::Clone() const
{
	return new XilinxMapIgnoreHierarchyFlag;
}

string XilinxMapIgnoreHierarchyFlag::toString(const XilinxFPGAToolchain* /*toolchain*/)
{
	return "-ignore_keep_hierarchy ";
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

XilinxMapRegisterDuplicationFlag::XilinxMapRegisterDuplicationFlag()
{
}

XilinxMapRegisterDuplicationFlag::~XilinxMapRegisterDuplicationFlag()
{
}

XilinxFPGAMapFlag* XilinxMapRegisterDuplicationFlag::Clone() const
{
	return new XilinxMapRegisterDuplicationFlag;
}

string XilinxMapRegisterDuplicationFlag::toString(const XilinxFPGAToolchain* /*toolchain*/)
{
	return "-register_duplication on ";
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

XilinxMapLogicOptimizationFlag::XilinxMapLogicOptimizationFlag()
{
}

XilinxMapLogicOptimizationFlag::~XilinxMapLogicOptimizationFlag()
{
}

XilinxFPGAMapFlag* XilinxMapLogicOptimizationFlag::Clone() const
{
	return new XilinxMapLogicOptimizationFlag;
}

string XilinxMapLogicOptimizationFlag::toString(const XilinxFPGAToolchain* /*toolchain*/)
{
	return "-logic_opt on ";
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

XilinxMapLutCombiningFlag::XilinxMapLutCombiningFlag(XilinxMapLutCombiningFlag::LutMode mode)
	: m_mode(mode)
{
}

XilinxMapLutCombiningFlag::~XilinxMapLutCombiningFlag()
{
}

XilinxFPGAMapFlag* XilinxMapLutCombiningFlag::Clone() const
{
	return new XilinxMapLutCombiningFlag(m_mode);
}

string XilinxMapLutCombiningFlag::toString(const XilinxFPGAToolchain* /*toolchain*/)
{
	switch(m_mode)
	{
	case LUT_COMBINE_OFF:
		return "-lc off ";
		
	case LUT_COMBINE_AREA:
		return "-lc area ";
		
	case LUT_COMBINE_AUTO:
		return "-lc auto ";
		
	default:
		FatalError("XilinxMapLutCombiningFlag: unknown value\n");
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// XilinxMapRegisterPackingFlag

XilinxMapRegisterPackingFlag::XilinxMapRegisterPackingFlag(int buswidth)
	: m_buswidth(buswidth)
{
}

XilinxMapRegisterPackingFlag::~XilinxMapRegisterPackingFlag()
{
}

XilinxFPGAMapFlag* XilinxMapRegisterPackingFlag::Clone() const
{
	return new XilinxMapRegisterPackingFlag(m_buswidth);
}

string XilinxMapRegisterPackingFlag::toString(const XilinxFPGAToolchain* /*toolchain*/)
{
	switch(m_buswidth)
	{
	case 0:
		return "-r off ";
		
	case 4:
		return "-r 4 ";

	case 8:
		return "-r 8 ";
		
	default:
		FatalError("XilinxMapRegisterPackingFlag: unknown value\n");
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// XilinxMapPlacementSeedFlag

XilinxMapPlacementSeedFlag::XilinxMapPlacementSeedFlag(int seed)
	: m_seed(seed)
{
}

XilinxMapPlacementSeedFlag::~XilinxMapPlacementSeedFlag()
{
}

XilinxFPGAMapFlag* XilinxMapPlacementSeedFlag::Clone() const
{
	return new XilinxMapPlacementSeedFlag(m_seed);
}

string XilinxMapPlacementSeedFlag::toString(const XilinxFPGAToolchain* /*toolchain*/)
{
	char tmp[128];
	snprintf(tmp, sizeof(tmp), "-t %d ", m_seed);
	return string(tmp);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// XilinxMapRegisterPackingFlag

XilinxSPIBusWidthFlag::XilinxSPIBusWidthFlag(int buswidth)
	: m_buswidth(buswidth)
{
}

XilinxSPIBusWidthFlag::~XilinxSPIBusWidthFlag()
{
}

FPGABitstreamGenerationFlag* XilinxSPIBusWidthFlag::Clone() const
{
	return new XilinxSPIBusWidthFlag(m_buswidth);
}

string XilinxSPIBusWidthFlag::toString(const FPGAToolchain* toolchain)
{
	if(dynamic_cast<const XilinxFPGAToolchain*>(toolchain) == NULL)
		FatalError("XilinxSPIBusWidthFlag is only supported on Xilinx FPGAs\n");

	switch(m_buswidth)
	{
	case 1:
		return "-g SPI_buswidth:1\n";
		
	case 2:
		return "-g SPI_buswidth:2\n";

	case 4:
		return "-g SPI_buswidth:4\n";
		
	default:
		FatalError("XilinxSPIBusWidthFlag: unsupported value (valid options are 1/2/4)\n");
	}
}

